#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<fcntl.h>
#include<aio.h>
#include<unistd.h>
 

 /**
  *     当我们的异步io操作完成的时候，我们可以通过信号通知我们的进程　也可以　用回调函数来进行异步通知，
  * 
  *     这个demo是用回调函数来进行异步通知，
  * 
 */
#define BUFFER_SIZE 1024
 
 
void aio_completion_handler(sigval_t sigval)
{
    //用来获取读aiocb结构的指针
    struct aiocb *prd;
    int ret;
 
    prd = (struct aiocb *)sigval.sival_ptr;//取出传入的　&rd
 
    printf("读到的内容:%s\n", prd->aio_buf);
 
    //判断请求是否成功
    if(aio_error(prd) == 0)
    {
        //获取返回值
        ret = aio_return(prd);
        printf("读返回值为:%d\n",ret);
    }

    //在次触发读，构成一个循环读  发现每次都从开头开始读，如果不从开头开始读的话，需要设置　rd.aio_offset = 累加和;
    ret = aio_read(prd);
    if(ret < 0)
    {
        perror("aio_read");
    }
}
 
int main(int argc,char **argv)
{
    int fd,ret;
    struct aiocb rd;
 
    fd = open("test.txt",O_RDONLY);
    if(fd < 0)
    {
        perror("test.txt");
    }
 
 
 
    //填充aiocb的基本内容
    bzero(&rd,sizeof(rd));
 
    rd.aio_fildes = fd;
    rd.aio_buf = (char *)malloc(sizeof(BUFFER_SIZE + 1));
    rd.aio_nbytes = BUFFER_SIZE;
    rd.aio_offset = 0;
 
    //填充aiocb中有关回调通知的结构体sigevent
    rd.aio_sigevent.sigev_notify = SIGEV_THREAD;//使用线程回调通知
    rd.aio_sigevent.sigev_notify_function = aio_completion_handler;//设置回调函数
    rd.aio_sigevent.sigev_notify_attributes = NULL;//使用默认属性
    rd.aio_sigevent.sigev_value.sival_ptr = &rd;//在aiocb控制块中加入自己的引用  这里传入&rd,在回调函数中我们可以使用
 
    //异步读取文件
    ret = aio_read(&rd);
    if(ret < 0)
    {
        perror("aio_read");
    }
 
    printf("异步读以开始\n");
    while(1);
    printf("异步读结束\n");
 
 
 
    return 0;
}

/**
 * 这种方式和使用 异步通知的效果是一样的
 * 
 * 异步通知，需要驱动去主动发　SIGIO信号，需要驱动支持
 * 
 * 而这个demo中的例子中，不需要底层的驱动去发送信号，而是在glibc中利用线程技术实现的。用的是一种回调的技术，值得参考和学习
 * 
*/